package com.shehuan.niv;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;

public class NiceImageView extends AppCompatImageView {
    private Context context;

    private boolean isCircle; // 是否显示为圆形，如果为圆形则设置的corner无效
    private boolean isCoverSrc; // stroke、inner_stroke是否覆盖图片
    private int strokeWidth; // 边框宽度
    private int strokeColor = Color.WHITE; // 边框颜色
    private int innerstrokeWidth; // 内层边框宽度
    private int innerstrokeColor = Color.WHITE; // 内层边框充色

    private int cornerRadius; // 统一设置圆角半径，优先级高于单独设置每个角的半径
    private int cornerTopLeftRadius; // 左上角圆角半径
    private int cornerTopRightRadius; // 右上角圆角半径
    private int cornerBottomLeftRadius; // 左下角圆角半径
    private int cornerBottomRightRadius; // 右下角圆角半径

    private int maskColor; // 遮罩颜色

    private Xfermode xfermode;

    private int width;
    private int height;
    private float radius;
    private float ratio;
    private String ratioString;

    private float[] strokeRadii;
    private float[] srcRadii;


    private RectF ViewRectF; // 图片占的矩形区域
    private RectF srcRectF; // 图片占的矩形区域
    private RectF strokeRectF; // 边框的矩形区域
    private RectF innerStrokeRectF; // 边框的矩形区域

    private Paint paint;
    private Path path; // 用来裁剪图片的ptah
    private Path srcPath; // 图片区域大小的path

    public NiceImageView(Context context) {
        this(context, null);
    }

    public NiceImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NiceImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.context = context;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NiceImageView, 0, 0);
        for (int i = 0; i < ta.getIndexCount(); i++) {
            int attr = ta.getIndex(i);
            if (attr == R.styleable.NiceImageView_niv_is_cover_src) {
                isCoverSrc = ta.getBoolean(attr, isCoverSrc);
            } else if (attr == R.styleable.NiceImageView_niv_is_circle) {
                isCircle = ta.getBoolean(attr, isCircle);
            } else if (attr == R.styleable.NiceImageView_niv_stroke_width) {
                strokeWidth = ta.getDimensionPixelSize(attr, strokeWidth);
            } else if (attr == R.styleable.NiceImageView_niv_stroke_color) {
                strokeColor = ta.getColor(attr, strokeColor);
            } else if (attr == R.styleable.NiceImageView_niv_inner_stroke_width) {
                innerstrokeWidth = ta.getDimensionPixelSize(attr, innerstrokeWidth);
            } else if (attr == R.styleable.NiceImageView_niv_inner_stroke_color) {
                innerstrokeColor = ta.getColor(attr, innerstrokeColor);
            } else if (attr == R.styleable.NiceImageView_niv_corner_radius) {
                cornerRadius = ta.getDimensionPixelSize(attr, cornerRadius);
            } else if (attr == R.styleable.NiceImageView_niv_corner_top_left_radius) {
                cornerTopLeftRadius = ta.getDimensionPixelSize(attr, cornerTopLeftRadius);
            } else if (attr == R.styleable.NiceImageView_niv_corner_top_right_radius) {
                cornerTopRightRadius = ta.getDimensionPixelSize(attr, cornerTopRightRadius);
            } else if (attr == R.styleable.NiceImageView_niv_corner_bottom_left_radius) {
                cornerBottomLeftRadius = ta.getDimensionPixelSize(attr, cornerBottomLeftRadius);
            } else if (attr == R.styleable.NiceImageView_niv_corner_bottom_right_radius) {
                cornerBottomRightRadius = ta.getDimensionPixelSize(attr, cornerBottomRightRadius);
            } else if (attr == R.styleable.NiceImageView_niv_mask_color) {
                maskColor = ta.getColor(attr, maskColor);
            } else if (attr == R.styleable.NiceImageView_niv_ratio) {
                ratioString = ta.getString(attr);

                try {
                    assert ratioString != null;
                    if (ratioString.contains(":")) {
                        String[] split = ratioString.split(":");
                        ratio = Float.parseFloat(split[0]) / Float.parseFloat(split[1]);
                    } else {
                        ratio = Float.parseFloat(ratioString);
                    }

                } catch (Exception e) {

                }


            }
        }
        ta.recycle();

        strokeRadii = new float[8];
        srcRadii = new float[8];

        strokeRectF = new RectF();
        innerStrokeRectF = new RectF();
        ViewRectF = new RectF();
        srcRectF = new RectF();

        paint = new Paint();
        path = new Path();

        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        } else {
            xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
            srcPath = new Path();
        }

        calculateRadii();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;

        initstrokeRectF();
        initSrcRectF();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获取宽度的模式和尺寸
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        //获取高度的模式和尺寸
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //宽确定，高不确定
        if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY && ratio != 0) {
            heightSize = (int) (widthSize / ratio + 0.5f);//根据宽度和比例计算高度
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        } else if (widthMode != MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY & ratio != 0) {
            widthSize = (int) (heightSize * ratio + 0.5f);
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        // 使用图形混合模式来显示指定区域的图片
        canvas.saveLayer(srcRectF, null, Canvas.ALL_SAVE_FLAG);
        if (!isCoverSrc) {
            float sx = 1.0f * (width - 2 * strokeWidth - 2 * innerstrokeWidth) / width;
            float sy = 1.0f * (height - 2 * strokeWidth - 2 * innerstrokeWidth) / height;
            // 缩小画布，使图片内容不被strokes覆盖
            canvas.scale(sx, sy, width / 2.0f, height / 2.0f);
        }
        super.onDraw(canvas);
        paint.reset();
        path.reset();
        if (isCircle) {
            path.addCircle(width / 2.0f, height / 2.0f, radius, Path.Direction.CCW);
        } else {
            path.addRoundRect(srcRectF, srcRadii, Path.Direction.CCW);
        }

        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setXfermode(xfermode);
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            canvas.drawPath(path, paint);
        } else {
            srcPath.reset();
            srcPath.addRect(srcRectF, Path.Direction.CCW);
            // 计算tempPath和path的差集
            srcPath.op(path, Path.Op.DIFFERENCE);
            canvas.drawPath(srcPath, paint);
        }
        paint.setXfermode(null);

        // 绘制遮罩
        if (maskColor != 0) {
            paint.setColor(maskColor);
            canvas.drawPath(path, paint);
        }
        // 恢复画布
        canvas.restore();
        // 绘制边框
        drawstrokes(canvas);
    }

    private void drawstrokes(Canvas canvas) {
        if (isCircle) {
            if (strokeWidth > 0) {
                drawCirclestroke(canvas, strokeWidth, strokeColor, radius - strokeWidth / 2.0f);
            }
            if (innerstrokeWidth > 0) {
                drawCirclestroke(canvas, innerstrokeWidth, innerstrokeColor, radius - strokeWidth - innerstrokeWidth / 2.0f);
            }
        } else {
            if (strokeWidth > 0) {
                paint.setColor(strokeColor);
                Path path1 = new Path();
                path1.addRoundRect(ViewRectF, strokeRadii, Path.Direction.CCW);
                Path path2 = new Path();
                path2.addRoundRect(strokeRectF, strokeRadii, Path.Direction.CCW);
                path1.op(path2, Path.Op.DIFFERENCE);
                canvas.drawPath(path1, paint);
            }

            if (innerstrokeWidth > 0) {
                paint.setColor(innerstrokeColor);
                Path path1 = new Path();
                path1.addRoundRect(strokeRectF, strokeRadii, Path.Direction.CCW);
                Path path2 = new Path();
                path2.addRoundRect(innerStrokeRectF, strokeRadii, Path.Direction.CCW);
                path1.op(path2, Path.Op.DIFFERENCE);
                canvas.drawPath(path1, paint);
            }

        }
    }

    private void drawCirclestroke(Canvas canvas, int strokeWidth, int strokeColor, float radius) {
        initstrokePaint(strokeWidth, strokeColor);
        path.addCircle(width / 2.0f, height / 2.0f, radius, Path.Direction.CCW);
        canvas.drawPath(path, paint);
    }

    private void drawRectFstroke(Canvas canvas, int strokeWidth, int strokeColor, RectF rectF, float[] radii) {
        initstrokePaint(strokeWidth, strokeColor);
        path.addRoundRect(rectF, radii, Path.Direction.CCW);
        canvas.drawPath(path, paint);
    }

    private void initstrokePaint(int strokeWidth, int strokeColor) {
        path.reset();
        paint.setStrokeWidth(strokeWidth);
        paint.setColor(strokeColor);
        paint.setStyle(Paint.Style.STROKE);
    }

    /**
     * 计算外边框的RectF
     */
    private void initstrokeRectF() {
        if (!isCircle) {
            strokeRectF.set(strokeWidth, strokeWidth, width - strokeWidth, height - strokeWidth);
            innerStrokeRectF.set(strokeWidth + innerstrokeWidth, strokeWidth + innerstrokeWidth, width - strokeWidth - innerstrokeWidth, height - strokeWidth - innerstrokeWidth);
        }
    }

    /**
     * 计算图片原始区域的RectF
     */
    private void initSrcRectF() {
        ViewRectF.set(0, 0, width, height);
        if (isCircle) {
            radius = Math.min(width, height) / 2.0f;
            srcRectF.set(width / 2.0f - radius, height / 2.0f - radius, width / 2.0f + radius, height / 2.0f + radius);
        } else {
            srcRectF.set(0, 0, width, height);
            if (isCoverSrc) {
                srcRectF.set(strokeWidth + innerstrokeWidth, strokeWidth + innerstrokeWidth, width - strokeWidth - innerstrokeWidth, height - strokeWidth - innerstrokeWidth);
            }
        }
    }

    /**
     * 计算RectF的圆角半径
     */
    private void calculateRadii() {
        if (isCircle) {
            return;
        }
        if (cornerRadius > 0) {
            for (int i = 0; i < strokeRadii.length; i++) {
                strokeRadii[i] = cornerRadius;
                srcRadii[i] = cornerRadius - strokeWidth / 2.0f;
            }
        } else {
            strokeRadii[0] = strokeRadii[1] = cornerTopLeftRadius;
            strokeRadii[2] = strokeRadii[3] = cornerTopRightRadius;
            strokeRadii[4] = strokeRadii[5] = cornerBottomRightRadius;
            strokeRadii[6] = strokeRadii[7] = cornerBottomLeftRadius;

            srcRadii[0] = srcRadii[1] = cornerTopLeftRadius - strokeWidth / 2.0f;
            srcRadii[2] = srcRadii[3] = cornerTopRightRadius - strokeWidth / 2.0f;
            srcRadii[4] = srcRadii[5] = cornerBottomRightRadius - strokeWidth / 2.0f;
            srcRadii[6] = srcRadii[7] = cornerBottomLeftRadius - strokeWidth / 2.0f;
        }
    }

    private void calculateRadiiAndRectF(boolean reset) {
        if (reset) {
            cornerRadius = 0;
        }
        calculateRadii();
        initstrokeRectF();
        invalidate();
    }


    public void isCoverSrc(boolean isCoverSrc) {
        this.isCoverSrc = isCoverSrc;
        initSrcRectF();
        invalidate();
    }

    public void isCircle(boolean isCircle) {
        this.isCircle = isCircle;
        initSrcRectF();
        invalidate();
    }

    public void setstrokeWidth(int strokeWidth) {
        this.strokeWidth = Utils.dp2px(context, strokeWidth);
        calculateRadiiAndRectF(false);
    }

    public void setstrokeColor(@ColorInt int strokeColor) {
        this.strokeColor = strokeColor;
        invalidate();
    }

    public void setInnerstrokeWidth(int innerstrokeWidth) {
        this.innerstrokeWidth = Utils.dp2px(context, innerstrokeWidth);
        invalidate();
    }

    public void setInnerstrokeColor(@ColorInt int innerstrokeColor) {
        this.innerstrokeColor = innerstrokeColor;
        invalidate();
    }

    public void setCornerRadius(int cornerRadius) {
        this.cornerRadius = Utils.dp2px(context, cornerRadius);
        calculateRadiiAndRectF(false);
    }

    public void setCornerTopLeftRadius(int cornerTopLeftRadius) {
        this.cornerTopLeftRadius = Utils.dp2px(context, cornerTopLeftRadius);
        calculateRadiiAndRectF(true);
    }

    public void setCornerTopRightRadius(int cornerTopRightRadius) {
        this.cornerTopRightRadius = Utils.dp2px(context, cornerTopRightRadius);
        calculateRadiiAndRectF(true);
    }

    public void setCornerBottomLeftRadius(int cornerBottomLeftRadius) {
        this.cornerBottomLeftRadius = Utils.dp2px(context, cornerBottomLeftRadius);
        calculateRadiiAndRectF(true);
    }

    public void setCornerBottomRightRadius(int cornerBottomRightRadius) {
        this.cornerBottomRightRadius = Utils.dp2px(context, cornerBottomRightRadius);
        calculateRadiiAndRectF(true);
    }

    public void setMaskColor(@ColorInt int maskColor) {
        this.maskColor = maskColor;
        invalidate();
    }

    public void setRatio(float ratio) {
        this.ratio = ratio;
        invalidate();
    }
}
